

#include <material/io/materialxmlexporter.h>
#include <material/io/materialreader.h>
#include <material/io/materialwriter.h>
#include <material/materialmanager.h>
#include <qstringlist.h>
#include <assert.h>
#include <qfileinfo.h>
#include <session.h>


/* ***************************************
 *     cceMaterialItem
 * ***************************************/

cceMaterialItem::cceMaterialItem (ItemType type)
  : QObject ()
  , _type (type)
  , _new (false)
{
}

cceMaterialItem::ItemType cceMaterialItem::GetItemType () const
{
  return _type;
}

void cceMaterialItem::Remove()
{
  emit Removed ();
}

void cceMaterialItem::Change()
{
  emit Changed ();
}


/* ***************************************
 *     cceMaterialTexture
 * ***************************************/

cceMaterialTexture::cceMaterialTexture ()
  : cceMaterialItem (ItemType_Texture)
  , _name ("")
  , _filename ("")
  , _group ("")
  , _wrap (Wrap_Repeat)
{
}

QString cceMaterialTexture::GetWrapName(Wrap wrap)
{
  switch (wrap)
    {
    case Wrap_Clamp:
      return "Clamp";
    case Wrap_Repeat:
      return "Repeat";
    default:
      break;
    }

  return "";
}

cceMaterialTexture::Wrap cceMaterialTexture::GetWrap(const QString &wrap)
{
  if (QString ("clamp").compare(wrap, Qt::CaseInsensitive) == 0)
    {
      return Wrap_Clamp;
    }
  else if (QString ("repeat").compare(wrap, Qt::CaseInsensitive) == 0)
    {
      return Wrap_Repeat;
    }
  return Wrap_Repeat;
}

/* ***************************************
 *     cceMaterialShader
 * ***************************************/

cceMaterialShader::cceMaterialShader ()
  : cceMaterialItem (ItemType_Shader)
{
}

QString cceMaterialShader::GetTypeName(Type type)
{
  switch (type)
    {
    case Type_Vertex:
      return "Vertex Shader";
    case Type_Fragment:
      return "Fragment Shader";
    case Type_Geometry:
      return "Geometry Shader";
    case Type_TesselationControl:
      return "Tesselation Control Shader";
    case Type_TesselationEval:
      return "Tesselation Evaluation Shader";
    }

  return QString::Null();
}

cceMaterialShader::Type cceMaterialShader::GetType(const QString& typeName)
{
	if (QString("vertex shader").compare(typeName, Qt::CaseInsensitive) == 0)
		{
			return Type_Vertex;
		}
	else if (QString("fragment shader").compare(typeName, Qt::CaseInsensitive) == 0)
		{
			return Type_Fragment;
		}
	else if (QString("geometry, shader").compare(typeName, Qt::CaseInsensitive) == 0)
		{
			return Type_Geometry;
		}
	else if (QString("tesselation control shader").compare(typeName, Qt::CaseInsensitive) == 0)
		{
			return Type_TesselationControl;
		}
	else if (QString("tesselation evaluation shader").compare(typeName, Qt::CaseInsensitive) == 0)
		{
			return Type_TesselationEval;
		}
	return Type_Vertex;
}

QString cceMaterialShader::GetBindingName(BindingType bindingType)
{
  switch (bindingType)
    {
    case BindingType_Attribute:
      return "Attribute";
    case BindingType_In:
      return "In";
    case BindingType_InOut:
      return "InOut";
    case BindingType_Uniform:
      return "Uniform";
    }
  return QString::Null();
}

void cceMaterialShader::UpdateBindings()
{
  QList<Binding> bindings;
  bindings.clear();

  QStringList dstParts;
  QStringList parts = _code.split ("\n");
  QRegExp rx_comment ("(#|//).*");
  foreach (QString part, parts)
    {
      part = part.replace(rx_comment, "");
      dstParts.append(part);
    }
  QString code = dstParts.join("\n");

  parts = code.split(";");
  QRegExp rx_no_binding ("^\\s*(attribute|uniform|in|inout)\\s+(\\w+)\\s+(\\w+)\\s*$");
  QRegExp rx_binding ("^\\s*(attribute|uniform|in|inout)\\s+(\\w+)\\s+(\\w+)\\s*:\\s*(\\w+)\\s*$");
  foreach (QString part, parts)
    {
      int pos = rx_binding.indexIn(part);
      if (pos >= 0)
        {
          QStringList list = rx_binding.capturedTexts();
          if (list.size() == 5)
            {
              RegisterBinding(bindings, list[1], list[2], list[3], list[4]);
            }
        }
      else
        {
          pos = rx_no_binding.indexIn(part);
          if (pos >= 0)
            {
              QStringList list = rx_no_binding.capturedTexts();
              if (list.size() == 4)
                {
                  RegisterBinding(bindings, list[1], list[2], list[3], "");
                }
            }
        }
    }

  if (bindings == _bindings)
    {
      return;
    }

  _bindings = bindings;
  emit BindingsChanged();
}

void cceMaterialShader::RegisterBinding(QList<Binding> &bindings, const QString &bindingType, const QString &typeName, const QString &name, const QString &semantic)
{
  BindingType type;
  if (bindingType == "attribute")
    {
      type = BindingType_Attribute;
    }
  else if (bindingType == "uniform")
    {
      type = BindingType_Uniform;
    }
  else if (bindingType == "in")
    {
      type = BindingType_In;
    }
  else if (bindingType == "inout")
    {
      type = BindingType_InOut;
    }
  else
    {
      return;
    }

  Binding binding;
  binding.bindingType = type;
  binding.bindingTypeString = bindingType;
  binding.typeName = typeName;
  binding.variable = name;
  binding.semantic = semantic;
  bindings.append(binding);
}

unsigned cceMaterialShader::GetNumberOfBindings() const
{
  return _bindings.size();
}

const QString& cceMaterialShader::GetBindingVariable(unsigned idx)
{
  assert (idx >= 0 && idx < _bindings.size());
  return _bindings[idx].variable;
}

const QString& cceMaterialShader::GetBindingSemantic(unsigned idx)
{
  assert (idx >= 0 && idx < _bindings.size());
  return _bindings[idx].semantic;
}


cceMaterialShader::BindingType cceMaterialShader::GetBindingType (unsigned idx)
{
  assert (idx >= 0 && idx < _bindings.size());
  return _bindings[idx].bindingType;
}


bool cceMaterialShader::Binding::operator ==(const cceMaterialShader::Binding& other) const
{
  return bindingType == other.bindingType
      && variable == other.variable
      && semantic == other.semantic;
}

QString cceMaterialShader::GetExportCode() const
{
  QString code = _code;
  foreach (Binding binding, _bindings)
    {
      QString regExp = binding.bindingTypeString + "\\s*" + binding.typeName + "\\s*" + binding.variable + "\\s*:\\s*" + binding.semantic + "\\s*;";
      printf ("Testing RegExp '%s'\n", (const char*)regExp.toAscii());
      fflush(stdout);
      QRegExp rx (regExp);


      QString replace = binding.bindingTypeString + " " + binding.typeName + " " + binding.variable + ";";
      code = code.replace (rx, replace);

    }

  return code;
}

QString cceMaterialShader::GetVersionString() const
{
  QRegExp versionRegExp ("^#version\\s*(\\d{3})$");
  QStringList parts = _code.split("\n");
  foreach (QString part, parts)
    {
      part = part.trimmed();
      int pos = versionRegExp.indexIn(part);
      if (pos >= 0)
        {
          QStringList list = versionRegExp.capturedTexts();
          return list[1];
        }
      if(part.length() > 0)
        {
          break;
        }
    }
  return "";
}


/* ***************************************
 *     cceMaterialProgramTechnique
 * ***************************************/

cceMaterialProgramTechnique::cceMaterialProgramTechnique ()
	: QObject ()
{
}

cceMaterialProgramTechnique::~cceMaterialProgramTechnique ()
{
	_shaders.clear ();
}

void cceMaterialProgramTechnique::AddShader (cceMaterialShader* shader)
{
  if (shader)
    {
      connect (shader, SIGNAL(Changed()), this, SIGNAL(Changed()));
      connect (shader, SIGNAL(Removed()), this, SLOT(onShaderRemoved()));
      _shaders.push_back (shader);
    }
  emit Changed ();
}

void cceMaterialProgramTechnique::RemoveShader (cceMaterialShader *shader)
{
  if (shader)
    {
      disconnect (shader, SIGNAL(Changed()), this, SIGNAL(Changed()));
      disconnect (shader, SIGNAL(Removed()), this, SLOT(onShaderRemoved()));
      _shaders.removeAll (shader);
    }
  emit Changed ();
}

void cceMaterialProgramTechnique::onShaderRemoved()
{
  cceMaterialShader *shader = static_cast<cceMaterialShader*>(sender ());
  RemoveShader(shader);
}


unsigned cceMaterialProgramTechnique::GetNumberOfShaders () const
{
	return _shaders.size ();
}

cceMaterialShader* cceMaterialProgramTechnique::GetShader (unsigned idx) const
{
	return _shaders[idx];
}


/* ***************************************
 *     cceMaterialProgram
 * ***************************************/

cceMaterialProgram::cceMaterialProgram ()
  : cceMaterialItem (ItemType_Program)
{
  _techniques.clear();
}

void cceMaterialProgram::Clear ()
{
	foreach (cceMaterialProgramTechnique* technique, _techniques)
		{
			technique->deleteLater ();
		}
	_techniques.clear ();
}

void cceMaterialProgram::AddTechnique (cceMaterialProgramTechnique *technique)
{
  if (technique)
    {
      connect (technique, SIGNAL(Changed()), this, SIGNAL(Changed()));
      _techniques.push_back (technique);
    }

  emit Changed();
}

void cceMaterialProgram::RemoveTechnique (cceMaterialProgramTechnique *technique)
{
  if (technique)
    {
      disconnect (technique, SIGNAL(Changed()), this, SIGNAL(Changed()));
      _techniques.removeAll (technique);
    }
  emit Changed ();
}

unsigned cceMaterialProgram::GetNumberOfTechniques () const
{
	return _techniques.size ();
}

cceMaterialProgramTechnique* cceMaterialProgram::GetTechnique (unsigned idx) const
{
	return _techniques[idx];
}




/* ***************************************
 *     cceMaterialEffectStage
 * ***************************************/


cceMaterialEffectStage::cceMaterialEffectStage ()
  : QObject ()
{
  Clear ();
}

cceMaterialEffectStage::~cceMaterialEffectStage ()
{
  Clear ();
}

void cceMaterialEffectStage::Clear ()
{
  for (unsigned i=0; i<Pass_COUNT; ++i)
    {
      _passes[i] = 0;
    }

}


void cceMaterialEffectStage::SetPass(cceMaterialPass pass, cceMaterialProgram *program)
{
  if (_passes[pass])
    {
      disconnect (_passes[pass], SIGNAL(Changed()), this, SIGNAL(Changed()));
      disconnect (_passes[pass], SIGNAL(Removed()), this, SLOT(onProgramRemoved()));
    }
  _passes[pass] = program;
  if (_passes[pass])
    {
      connect (_passes[pass], SIGNAL(Changed()), this, SIGNAL(Changed()));
      connect (_passes[pass], SIGNAL(Removed()), this, SLOT(onProgramRemoved()));
    }
  emit Changed();
}

void cceMaterialEffectStage::onProgramRemoved()
{
  cceMaterialProgram *program = static_cast<cceMaterialProgram*>(sender ());
  for (unsigned i=0; i<Pass_COUNT; ++i)
    {
      if (_passes[i] == program)
        {
          SetPass((cceMaterialPass)i, 0);
          return;
        }
    }
}

cceMaterialProgram* cceMaterialEffectStage::GetPass(cceMaterialPass pass)
{
  return _passes[pass];
}

QString cceMaterialEffectStage::GetPassName(cceMaterialPass pass)
{
  switch (pass)
    {
    case Pass_Depth:
      return "Depth";

    case Pass_Diffuse:
      return "Diffuse";

    case Pass_Shadow:
      return "Shadow";

    case Pass_Stencil:
      return "Stencil";

    }

  return QString::Null ();
}


cceMaterialPass cceMaterialEffectStage::GetPass(const QString &passName)
{
	if (QString("depth").compare(passName, Qt::CaseInsensitive) == 0)
		{
			return Pass_Depth;
		}
	else if (QString("diffuse").compare(passName, Qt::CaseInsensitive) == 0)
		{
			return Pass_Diffuse;
		}
	else if (QString("shadow").compare(passName, Qt::CaseInsensitive) == 0)
		{
			return Pass_Shadow;
		}
	else if (QString("stencil").compare(passName, Qt::CaseInsensitive) == 0)
		{
			return Pass_Stencil;
		}
	return Pass_Diffuse;
}

/* ***************************************
 *     cceMaterialEffect
 * ***************************************/

cceMaterialEffect::cceMaterialEffect ()
  : cceMaterialItem (ItemType_Effect)
{
}


cceMaterialEffect::~cceMaterialEffect ()
{
  Clear ();
}


void cceMaterialEffect::Clear ()
{
  foreach (cceMaterialEffectStage* stage, _stages)
    {
      stage->deleteLater ();
    }
  _stages.clear ();
}

void cceMaterialEffect::AddStage(cceMaterialEffectStage *stage)
{
  if (stage)
    {
      connect (stage, SIGNAL(Changed()), this, SIGNAL(Changed()));
      _stages.push_back (stage);
    }
  emit Changed();
}

void cceMaterialEffect::RemoveStage(cceMaterialEffectStage *stage)
{
  if (stage)
    {
      disconnect (stage, SIGNAL(Changed()), this, SIGNAL(Changed()));
      _stages.removeAll (stage);
    }
  emit Changed();
}

unsigned cceMaterialEffect::GetNumberOfStages() const
{
  return _stages.size ();
}

cceMaterialEffectStage* cceMaterialEffect::GetStage(unsigned idx) const
{
  return _stages[idx];
}

namespace
{
void add_effect_texture_binding (QList<cceEffectTextureBindings>& bindings, cceMaterialPass pass, unsigned unit, const QString& name)
{
  for (unsigned i=0, j=bindings.size(); i<j; ++i)
    {
      cceEffectTextureBindings& binding = bindings[i];
      if (binding.Pass == pass &&
          binding.Unit == unit)
        {
          binding.Bindings << name;
          return;
        }
    }

  // not found: so attach a new binding.s
  cceEffectTextureBindings binding;
  binding.Unit = unit;
  binding.Pass = pass;
  binding.Bindings.clear();
  binding.Bindings <<name;
  bindings.push_back(binding);
}
}


QList<cceEffectTextureBindings> cceMaterialEffect::GetTextureRequirements() const
{
  QList<cceEffectTextureBindings> bindings;

  foreach (cceMaterialEffectStage* stage, _stages)
    {
      for (unsigned pass=0; pass<Pass_COUNT; ++pass)
        {
          cceMaterialProgram* program = stage->GetPass((cceMaterialPass)pass);
          if (!program)
            {
              continue;
            }

          for (unsigned t=0, num_techniques=program->GetNumberOfTechniques(); t<num_techniques; ++t)
            {
              cceMaterialProgramTechnique* technique = program->GetTechnique(t);

              if (!technique)
                {
                  continue;
                }

              for (unsigned s=0, num_shaders=technique->GetNumberOfShaders(); s<num_shaders; ++s)
                {
                  cceMaterialShader* shader = technique->GetShader(s);
                  if (!shader)
                    {
                      continue;
                    }

                  for (unsigned b=0, num_bindings=shader->GetNumberOfBindings(); b<num_bindings; ++b)
                    {
                      const QString& semantic = shader->GetBindingSemantic(b);
                      const QString& variable = shader->GetBindingVariable(b);

                      QRegExp texture_reg_exp ("TEXTURE(\\d+)");
                      if (texture_reg_exp.indexIn(semantic) < 0)
                        {
                          continue;
                        }


                      QStringList list = texture_reg_exp.capturedTexts();
                      if (list.size() != 2)
                        {
                          continue;
                        }

                      QString unitText = list[1];
                      bool ok;
                      int unit = unitText.toInt(&ok, 10);
                      if (!ok)
                        {
                          continue;
                        }

                      if (pass != Pass_Diffuse || unit != 7)
                        {
                          add_effect_texture_binding (bindings, (cceMaterialPass)pass, unit, variable);
                        }
                    }
                }
            }
        }

    }


  return bindings;
}


/* ***************************************
 *     cceMaterialTexturePack
 * ***************************************/

cceMaterialTexturePack::cceMaterialTexturePack ()
  : QObject ()
{
  for (unsigned i=0; i<16; ++i)
    {
      _textures[i] = 0;
    }
}

void cceMaterialTexturePack::SetTexture(unsigned unit, cceMaterialTexture *texture)
{
  if (_textures[unit])
    {
      disconnect(_textures[unit], SIGNAL(Changed()), this, SIGNAL(Changed()));
      disconnect(_textures[unit], SIGNAL(Removed()), this, SLOT(onTextureRemoved()));
    }
  _textures[unit] = texture;
  if (_textures[unit])
    {
      connect(_textures[unit], SIGNAL(Changed()), this, SIGNAL(Changed()));
      connect(_textures[unit], SIGNAL(Removed()), this, SLOT(onTextureRemoved()));
    }
  emit Changed();
}

cceMaterialTexture *cceMaterialTexturePack::GetTexture(unsigned unit)
{
  return _textures[unit];
}

void cceMaterialTexturePack::onTextureRemoved()
{
  cceMaterialTexture *texture = static_cast<cceMaterialTexture*>(sender ());
  for (unsigned i=0; i<16; ++i)
    {
      if (_textures[i] == texture)
        {
          SetTexture (i, 0);
          return;
        }
    }
}


/* ***************************************
 *     cceMaterialMaterial
 * ***************************************/

cceMaterialMaterial::cceMaterialMaterial ()
  : cceMaterialItem (ItemType_Material)
  , _ambient (0, 0, 0, 255)
  , _diffuse (255, 255, 255, 255)
  , _specular (255, 255, 255, 255)
  , _shininess (32.0f)
  , _emission (0, 0, 0, 255)
  , _effect (0)
  , _writeDepth (true)
  , _testDepth (true)
  , _transparent (false)
{
  for (unsigned i=0; i<Pass_COUNT; ++i)
    {
      connect (&_textures[i], SIGNAL(Changed()), this, SIGNAL(Changed()));
    }
}


cceMaterialMaterial::~cceMaterialMaterial()
{
  _effect = 0;
}

cceMaterialTexturePack* cceMaterialMaterial::GetTextures(cceMaterialPass pass)
{
  return &_textures[pass];
}

void cceMaterialMaterial::SetTexture(cceMaterialPass pass, unsigned unit, cceMaterialTexture *texture)
{
  _textures[pass].SetTexture(unit, texture);
}

cceMaterialTexture* cceMaterialMaterial::GetTexture(cceMaterialPass pass, unsigned unit)
{
  return _textures[pass].GetTexture(unit);
}




/* ***************************************
 *     cceMaterialManager
 * ***************************************/

cceMaterialManager::cceMaterialManager ()
	: QObject ()
{
	// add a few default shaders
  cceMaterialReader reader (this);
  reader.ReadAll ();
}

cceMaterialManager::~cceMaterialManager()
{
//  Save ();
}

void cceMaterialManager::SaveAll()
{
  cceMaterialWriter writer (this);
  writer.WriteAll();
}

void cceMaterialManager::SaveTextures()
{
  cceMaterialWriter writer (this);
  writer.WriteTextures();
}

void cceMaterialManager::SaveShaders()
{
  cceMaterialWriter writer (this);
  writer.WriteShaders();
}

void cceMaterialManager::SavePrograms()
{
  cceMaterialWriter writer (this);
  writer.WritePrograms();
}

void cceMaterialManager::SaveEffects()
{
  cceMaterialWriter writer (this);
  writer.WriteEffects();
}

void cceMaterialManager::SaveMaterials()
{
  cceMaterialWriter writer (this);
  writer.WriteMaterials();
}


void cceMaterialManager::Export()
{

	cceMaterialXMLExporter exporter (this);
	exporter.ExportAll();
}

void cceMaterialManager::UpdateGraphicsSystem ()
{
  // TODO: update the current bound crimson core engine.
}

void cceMaterialManager::Clear()
{
  ClearMaterials();
  ClearEffects();
  ClearPrograms();
  ClearShaders();
  ClearTextures();
}

void cceMaterialManager::onTextureChanged()
{
  cceMaterialTexture *texture = static_cast<cceMaterialTexture*>(sender());
  emit TextureChanged(texture);
}

void cceMaterialManager::AddTexture(cceMaterialTexture *texture)
{
  if (texture)
    {
			texture->SetNew(false);
      connect (texture, SIGNAL(Changed()), this, SLOT(onTextureChanged()));
      _textures.push_back(texture);
      emit TextureAdded (texture);
    }
}

void cceMaterialManager::ClearTextures()
{
  foreach (cceMaterialTexture* texture, _textures)
    {
      disconnect (texture, SIGNAL(Changed()), this, SLOT(onTextureChanged()));
      texture->Remove();
      emit TextureRemoved(texture);
    }
  _textures.clear();
}

void cceMaterialManager::RemoveTexture(cceMaterialTexture *texture)
{
  if (texture)
    {
      disconnect(texture, SIGNAL(Changed()), this, SLOT(onTextureChanged()));
      texture->Remove();
      _textures.removeAll(texture);
      emit TextureRemoved(texture);
    }
}

unsigned cceMaterialManager::GetNumberOfTextures() const
{
  return _textures.size();
}

cceMaterialTexture* cceMaterialManager::GetTexture (unsigned idx) const
{
  return _textures[idx];
}

cceMaterialTexture* cceMaterialManager::GetTexture(const QString &name) const
{
  Q_FOREACH(cceMaterialTexture* texture, _textures)
    {
      if (texture->GetName() == name)
        {
          return texture;
        }
    }
  return 0;
}

QString cceMaterialManager::CopyTextureFilename(const QString &filename)
{
	if (cceSession::Get()->CopyFileToProjectPath(filename, CCE_MATERIAL_TEXTURES_FOLDER).isNull())
		{
			return QString::Null();
		}

	QFileInfo fi (filename);
	QString internalFilename = fi.baseName();
	if (fi.completeSuffix().length() > 0)
		{
			internalFilename += "." + fi.completeSuffix();
		}

	return internalFilename;
}

cceMaterialTexture* cceMaterialManager::GetTextureByFilename(const QString &filename, bool add)
{
	QString internalFilename = CopyTextureFilename(filename);
	if (internalFilename.isNull())
		{
			return 0;
		}


  Q_FOREACH(cceMaterialTexture* texture, _textures)
    {
      if (texture->GetFilename() == internalFilename)
        {
          return texture;
        }
    }

	QFileInfo fi (filename);

  cceMaterialTexture* texture = new cceMaterialTexture ();
  texture->SetFilename(internalFilename.trimmed());
  texture->SetName(fi.baseName()+".tex");
	texture->SetNew(false);


  AddTexture(texture);

  return texture;
}


void cceMaterialManager::onShaderChanged()
{
  cceMaterialShader *shader = static_cast<cceMaterialShader*>(sender());
  emit ShaderChanged(shader);
}


void cceMaterialManager::ClearShaders()
{
  foreach (cceMaterialShader* shader, _shaders)
    {
      disconnect (shader, SIGNAL(Changed()), this, SLOT(onShaderChanged()));
      shader->Remove();
      emit ShaderRemoved(shader);
    }
  _shaders.clear();
}


void cceMaterialManager::AddShader (cceMaterialShader* shader)
{
  if (shader)
    {
			shader->SetNew(false);
      connect (shader, SIGNAL(Changed()), this, SLOT(onShaderChanged()));
      _shaders.push_back (shader);
      emit ShaderAdded(shader);
    }
}

void cceMaterialManager::RemoveShader (cceMaterialShader* shader)
{
  if (shader)
    {
      disconnect(shader, SIGNAL(Changed()), this, SLOT(onShaderChanged()));
      shader->Remove();
      _shaders.removeAll (shader);
      emit ShaderRemoved(shader);
    }
}

unsigned cceMaterialManager::GetNumberOfShaders () const
{
	return _shaders.size ();
}

cceMaterialShader* cceMaterialManager::GetShader (unsigned idx) const
{
	return _shaders[idx];
}

cceMaterialShader* cceMaterialManager::GetShader (const QString& name) const
{
	Q_FOREACH (cceMaterialShader* shader, _shaders)
		{
			if (shader->GetName () == name)
				{
					return shader;
				}
		}
	return 0;
}



void cceMaterialManager::ClearPrograms()
{
  foreach (cceMaterialProgram* program, _programs)
    {
      disconnect (program, SIGNAL(Changed()), this, SLOT(onProgramChanged()));
      program->Remove();
      emit ProgramRemoved(program);
    }
  _programs.clear();
}


void cceMaterialManager::onProgramChanged()
{
  cceMaterialProgram *program = static_cast<cceMaterialProgram*>(sender());
  emit ProgramChanged(program);
}

void cceMaterialManager::AddProgram (cceMaterialProgram *program)
{
  if (program)
    {
			program->SetNew(false);
      connect (program, SIGNAL(Changed()), this, SLOT(onProgramChanged()));
      _programs.push_back (program);
      emit ProgramAdded (program);
    }
}

void cceMaterialManager::RemoveProgram (cceMaterialProgram *program)
{
  if (program)
    {
      disconnect(program, SIGNAL(Changed()), this, SLOT(onProgramChanged()));
      program->Remove();
      _programs.removeAll (program);
      emit ProgramRemoved (program);
    }
}


unsigned cceMaterialManager::GetNumberOfPrograms () const
{
	return _programs.size ();
}

cceMaterialProgram* cceMaterialManager::GetProgram (unsigned idx) const
{
	return _programs[idx];
}



cceMaterialProgram* cceMaterialManager::GetProgram(const QString& name) const
{
	Q_FOREACH (cceMaterialProgram* program, _programs)
		{
			if (program->GetName () == name)
				{
					return program;
				}
		}
	return 0;
}


void cceMaterialManager::ClearEffects()
{
  foreach (cceMaterialEffect* effect, _effects)
    {
      disconnect (effect, SIGNAL(Changed()), this, SLOT(onEffectChanged()));
      effect->Remove();
      emit EffectRemoved(effect);
    }
  _effects.clear();
}


void cceMaterialManager::onEffectChanged()
{
  cceMaterialEffect *effect = static_cast<cceMaterialEffect*>(sender());
  emit EffectChanged(effect);
}

void cceMaterialManager::AddEffect(cceMaterialEffect *effect)
{
  if (effect)
    {
			effect->SetNew(false);
      connect (effect, SIGNAL(Changed()), this, SLOT(onEffectChanged()));
      _effects.push_back (effect);
      emit EffectAdded(effect);
    }
}

void cceMaterialManager::RemoveEffect(cceMaterialEffect *effect)
{
  if (effect)
    {
      disconnect (effect, SIGNAL(Changed()), this, SLOT(onEffectChanged()));
      effect->Remove();
      _effects.removeAll (effect);
      emit EffectRemoved(effect);
    }
}


unsigned cceMaterialManager::GetNumberOfEffects () const
{
  return _effects.size ();
}

cceMaterialEffect* cceMaterialManager::GetEffect(unsigned idx) const
{
  return _effects[idx];
}



cceMaterialEffect* cceMaterialManager::GetEffect(const QString& name) const
{
  Q_FOREACH (cceMaterialEffect* effect, _effects)
    {
      if (effect->GetName () == name)
        {
          return effect;
        }
    }
  return 0;
}


void cceMaterialManager::ClearMaterials()
{
  foreach (cceMaterialMaterial* material, _materials)
    {
      disconnect (material, SIGNAL(Changed()), this, SLOT(onMaterialChanged()));
      material->Remove();
      emit MaterialRemoved(material);
    }
  _materials.clear();
}


void cceMaterialManager::onMaterialChanged()
{
  cceMaterialMaterial *material = static_cast<cceMaterialMaterial*>(sender());
  emit MaterialChanged(material);
}

void cceMaterialManager::AddMaterial(cceMaterialMaterial *material)
{
  if (material)
    {
      material->SetNew(false);
      connect (material, SIGNAL(Changed()), this, SLOT(onMaterialChanged()));
      _materials.push_back (material);
      emit MaterialAdded(material);
    }
}

void cceMaterialManager::RemoveMaterial(cceMaterialMaterial *material)
{
  if (material)
    {
      disconnect (material, SIGNAL(Changed()), this, SLOT(onMaterialChanged()));
      material->Remove();
      _materials.removeAll (material);
      emit MaterialRemoved(material);
    }
}


unsigned cceMaterialManager::GetNumberOfMaterials() const
{
  return _materials.size ();
}

cceMaterialMaterial* cceMaterialManager::GetMaterial(unsigned idx) const
{
  return _materials[idx];
}



cceMaterialMaterial* cceMaterialManager::GetMaterial(const QString& name) const
{
  Q_FOREACH (cceMaterialMaterial* material, _materials)
    {
      if (material->GetName () == name)
        {
          return material;
        }
    }
  return 0;
}


